#pragma once

#include "Plotter.h"
#include <vector>

namespace HW2
{



	struct AntiAliazer : public Plotter 
	{
		std::vector<std::vector<Color> > data;
		int type, filterSize;

		std::vector<std::vector<double> > box3, box5, gauss3, gauss5, sync3, sync5, triangle3, triangle5;

		void initTriangles()
		{
			triangle3.resize(3);
			triangle5.resize(5);
			triangle3[0].push_back(1.0/16);
			triangle3[0].push_back(2.0/16);
			triangle3[0].push_back(1.0/16);
			triangle3[1].push_back(2.0/16);
			triangle3[1].push_back(4.0/16);
			triangle3[1].push_back(2.0/16);
			triangle3[2] = triangle3[0];

			triangle5[0].push_back(1.0/81);
			triangle5[0].push_back(2.0/81);
			triangle5[0].push_back(3.0/81);
			triangle5[0].push_back(2.0/81);
			triangle5[0].push_back(1.0/81);

			triangle5[1].push_back(2.0/81);
			triangle5[1].push_back(4.0/81);
			triangle5[1].push_back(6.0/81);
			triangle5[1].push_back(4.0/81);
			triangle5[1].push_back(2.0/81);

			triangle5[2].push_back(3.0/81);
			triangle5[2].push_back(6.0/81);
			triangle5[2].push_back(9.0/81);
			triangle5[2].push_back(6.0/81);
			triangle5[2].push_back(3.0/81);

			triangle5[3] = triangle5[1];
			triangle5[4] = triangle5[0];
		}
		void initBox()
		{
			box3.resize(3);
			for(int i = 0; i< 3; ++i)
				for(int j = 0; j <3; ++j)
					box3[i].push_back(1/9.0);
			box5.resize(5);
			for(int i = 0; i< 5; ++i)
				for(int j = 0; j <5; ++j)
					box5[i].push_back(1/25.0);
		}
		void initGauss()
		{
			gauss3.resize(3);
			gauss5.resize(5);

			gauss3[0].push_back(1.0/17);
			gauss3[0].push_back(2.0/17);
			gauss3[0].push_back(1.0/17);
			gauss3[1].push_back(2.0/17);
			gauss3[1].push_back(5.0/17);
			gauss3[1].push_back(2.0/17);
			gauss3[2] = gauss3[0];


			for(int i = 0; i < 5; ++i)
				gauss5[0].push_back(1.0/50);
			gauss5[1].push_back(1.0/50);
			gauss5[1].push_back(2.0/50);
			gauss5[1].push_back(4.0/50);
			gauss5[1].push_back(2.0/50);
			gauss5[1].push_back(1.0/50);

			gauss5[2].push_back(1.0/50);
			gauss5[2].push_back(4.0/50);
			gauss5[2].push_back(10.0/50);
			gauss5[2].push_back(4.0/50);
			gauss5[2].push_back(1.0/50);

			gauss5[3]= gauss5[1];
			gauss5[4]= gauss5[0];
		}
		void initSync() 
		{
			sync3.resize(3);
			sync5.resize(5);

			sync3[0].push_back(2.0/24);
			sync3[0].push_back(3.0/24);
			sync3[0].push_back(2.0/24);
			sync3[1].push_back(3.0/24);
			sync3[1].push_back(4.0/24);
			sync3[1].push_back(3.0/24);
			sync3[2] = sync3[0];

			sync5[0].push_back(-2.0/33);
			sync5[0].push_back(-1.0/33);
			sync5[0].push_back(0/33);
			sync5[0].push_back(-1.0/33);
			sync5[0].push_back(-2.0/33);
			
			sync5[1].push_back(-1.0/33);
			sync5[1].push_back(4.0/33);
			sync5[1].push_back(6.0/33);
			sync5[1].push_back(4.0/33);
			sync5[1].push_back(-1.0/33);

			sync5[2].push_back(0.0/33);
			sync5[2].push_back(6.0/33);
			sync5[2].push_back(9.0/33);
			sync5[2].push_back(6.0/33);
			sync5[2].push_back(0.0/33);

			sync5[3]= sync5[1];
			sync5[4]= sync5[0];


		}


		AntiAliazer(int width, int height, int type = 0, int filterSize = 0) : type(type), filterSize(filterSize)
		{
			resize(width, height);

			initTriangles();
			initBox();
			initGauss();
			initSync();
		}

		void resize(int width, int height)
		{
			data = std::vector<std::vector<Color> >(width, std::vector<Color>(height, Color(0,0,0)));
		}

		void filter(const std::vector<std::vector<double> > &f, int x, int y, Color c)
		{
			int i = x % filterSize;
			int j = y % filterSize;
			
			double r = f[i][j];
			
			add(x/filterSize,y/filterSize,c*r);
		}

		void plot(int x, int y, double z = 0, Color c = Color(), Coordinates orig = Coordinates())
		{
			if(filterSize == 1)
			{
				add(x,y,c);
				return;
			}

			switch(type)
			{
			case 1: //box
				if(filterSize == 3)
					filter(box3, x,y,c);
				else if(filterSize == 5)
					filter(box5, x,y,c);
				break;
			case 2:
				if(filterSize == 3)
					filter(gauss3, x,y,c);
				else if(filterSize == 5)
					filter(gauss5, x,y,c);
				break;
			case 3:
				if(filterSize == 3)
					filter(triangle3, x,y,c);
				else if(filterSize == 5)
					filter(triangle5, x,y,c);
				break;
			case 4: // sync
				if(filterSize == 3)
					filter(sync3, x,y,c);
				else if(filterSize == 5)
					filter(sync5, x,y,c);
				break;

			case 0: // none
			default:
				add(x,y,c);
				return;

			}
		}

		int add(int x, int y, Color c)
		{
			if(x >= 0 && x < data.size() && y >= 0 && y < data[0].size())
			{
				data[x][y] = data[x][y] + c;
				return 1;
			}
			return 0;
		}

		template<typename PLOTTER>
		void draw(PLOTTER &p)
		{
			for(int i = 0; i < data.size(); ++i)
			{
				for(int j = 0; j < data[0].size(); ++j)
				{
					p.plot(i,j,0,data[i][j]);				
				}
			}

		}
	};

}